//核心业务模块

//依赖
const dgram = require('node:dgram')
const { sleep } = require('./util')

//导出
module.exports = {
    createNTPServer
}

/*
    创建NTP服务实例，和定义class再实例化异曲同工
    参数config表示配置，结构: 
    {
        port: number, //服务端口
        type: string, //服务类型，默认值'udp4'
    }
    返回服务实例，结构：
    {
        startNTPServer: ()=>Promise<any>, //启动服务的方法，服务出错或被关闭时Promise得到结果
        stopNTPServer: ()=>undefined, //关闭服务的方法
    }
*/
function createNTPServer(config) {
    //内部的实例变量
    let server = null
    //服务是否已调用关闭
    let stopped = false
    //错误计数
    let errCount = 0
    return {
        //启动服务，服务出错时自动重启
        async startNTPServer() {
            //实例已创建则返回，防止重复创建
            if (server) return
            //已关闭的不再创建，退出递归循环的条件
            if (stopped) return
            //实例出错时递归重建服务
            try {
                //赋值
                server = createUDPServer(config)
                await server.onError
            } catch (err) {
                console.log(`server error at ${new Date().toLocaleString()}, errCount: ${++errCount}`)
                console.error(err)
                server = null
                await sleep(2000)
                this.startNTPServer()
            }
        },
        //关闭服务
        stopNTPServer() {
            if (stopped) return
            server?.stop()
            stopped = true
            server = null
        },
    }
}

/*
    创建UDP服务
    返回数据结构：
    {
        onError: Promise<any>, //服务出错时Promise得到结果
        stop: ()=>undefined, //关闭服务的方法
    }
*/
function createUDPServer(config) {
    const { type = 'udp4', port } = config
    //检查端口参数
    if (!port || port<0){
        throw new Error('bad port')
    }

    //创建服务udp套接字
    const server = dgram.createSocket(type)

    const startAt = Date.now()
    //error的回调转Promise，以便外层观察并重建服务
    const serverOnErr = new Promise((resolve, reject) => {
        //监听关闭
        server.on('close', () => {
            console.log(`server on close, the config is`, config)
            //回调返回服务的运行时长
            resolve({ workedMs: Date.now() - startAt })
        })
        //监听错误
        server.on('error', (err) => {
            //回调返回出错信息
            reject(err)
            //关闭服务
            server.close()
        })
    })
    //监听消息
    server.on('message', (msg, rinfo) => {
        const { address, port } = rinfo
        console.log(`server got request: ${msg} from ${address}:${port}`)
        const buf = getServerTimeV1()
        server.send(buf, port, address)
    })
    //监听绑定成功
    server.on('listening', () => {
        const address = server.address()
        console.log(`new server listening ${address.address}:${address.port}`)
    })
    //绑定端口
    server.bind(port)

    return {
        onError: serverOnErr,
        stop() {
            try {
                server.close()
            } catch (error) {

            }
        },
    }
}

//第1字节写入uint8型数字作为版本号，第2字节到第9字节（共8个字节）写入当前时间戳毫秒数。
//返回Buffer类型
function getServerTimeV1() {
    const version = 1
    const ts = Date.now()
    const buf = Buffer.alloc(9)
    buf.writeUInt8(version)
    //需求没提用大端还是小端，这里按网络常用的大端字节序写入，客户端也需要用相同字节序读取
    buf.writeBigUInt64BE(BigInt(ts), 1)
    return buf
}

// console.log(getServerTimeV1())